package top.ijiujiu.buried;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import top.ijiujiu.entity.SysBuriedLog;
import top.ijiujiu.entity.SysUser;
import top.ijiujiu.exception.DefaultException;
import top.ijiujiu.service.ISysBuriedLogService;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * MyMetaObjectHandler
 *
 * @Author 神秘的黄瓜
 * @Version 1.0
 * @Date 2020-04-01 11:46
 */
@Aspect
@Component
public class LogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class);

    @Autowired
    private ISysBuriedLogService sysBuriedLogService;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * Controller层切点
     */
    @Pointcut(value = "@annotation(top.ijiujiu.buried.MethodLog)")
    public void methodCachePointcut() {
    }

    /**
     * Controller执行方法环绕输出执行日志
     *
     * @param point
     * @throws Exception
     */
    @Around(value = "methodCachePointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (ObjectUtils.isEmpty(authentication) || ObjectUtils.isEmpty(authentication.getPrincipal())) {
            LOGGER.error("未找到用户信息~~~");
            throw new UsernameNotFoundException("未找到用户信息~~~");
        }
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            LOGGER.error("未找到HttpServletRequest~~~");
            throw new DefaultException(HttpStatus.INTERNAL_SERVER_ERROR, "未获取到HttpServletRequest~~~");
        }
        HttpServletRequest request = requestAttributes.getRequest();
        MethodLog methodLog = getMethodRemark(point);
        if (ObjectUtils.isEmpty(methodLog)) {
            LOGGER.error("未获取到MethodLog~~~");
            throw new DefaultException(HttpStatus.INTERNAL_SERVER_ERROR, "未获取到MethodLog~~~");
        }
        String monthName = point.getSignature().getName();
        String packages = point.getThis().getClass().getName();
        if (!StringUtils.isEmpty(packages) && packages.contains("$$Enhancer") && packages.contains("CGLIB$$")) {
            packages = packages.substring(0, packages.indexOf("$$"));
        }
        SysUser user = (SysUser) authentication.getPrincipal();
        SysBuriedLog sysBuriedLog = new SysBuriedLog();
        sysBuriedLog.setRecordTime(new Date());
        sysBuriedLog.setRecordIp(getRemoteHost(request));
        sysBuriedLog.setUserId(user.getId());
        sysBuriedLog.setUserName(user.getName());
        sysBuriedLog.setActionType(String.valueOf(methodLog.methodType()));
        sysBuriedLog.setActionPath(packages + "." + monthName);
        List<String> list = new ArrayList<>();
        Object[] args = point.getArgs();
        for (Object arg : args) {
            list.add(objectMapper.writeValueAsString(arg));
        }
        sysBuriedLog.setActionParam(StringUtils.collectionToCommaDelimitedString(list));
        sysBuriedLog.setActionDesc(methodLog.remark());
        sysBuriedLogService.save(sysBuriedLog);
        return point.proceed();
    }

    /**
     * 获取方法的中文备注，用于记录用户的操作日志描述
     *
     * @param joinPoint
     * @return
     * @throws ClassNotFoundException
     */
    private MethodLog getMethodRemark(JoinPoint joinPoint) throws ClassNotFoundException {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Method[] method = targetClass.getMethods();
        for (Method m : method) {
            if (m.getName().equals(methodName)) {
                Class<?>[] tmpCs = m.getParameterTypes();
                if (tmpCs.length == arguments.length) {
                    MethodLog methodCache = m.getAnnotation(MethodLog.class);
                    if (methodCache != null && !("").equals(methodCache.remark())) {
                        return methodCache;
                    }
                    break;
                }
            }
        }
        return null;
    }

    /**
     * 获取客户端IP地址
     *
     * @param request
     * @return
     */
    private String getRemoteHost(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ip;
    }
}

